package com.zwr.sheller.customer.Service.Impl;


import com.zwr.sheller.core.common.FLJBusinessException;
import com.zwr.sheller.core.common.FLJExceptionCode;
import com.zwr.sheller.core.pojo.AssociateGoods;
import com.zwr.sheller.core.pojo.Goods;
import com.zwr.sheller.core.pojo.GoodsVo;
import com.zwr.sheller.core.util.JsonUtil;
import com.zwr.sheller.customer.Service.IndixService;
import com.zwr.sheller.customer.Service.StoreShellerIndexService;
import com.zwr.sheller.customer.mapper.IndexMapper;
import com.zwr.sheller.customer.repository.GoodsVoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * @author 付龙江
 * @date 2022/1/12 17:27
 */
@Service("indixService")
@Transactional(rollbackFor = Throwable.class)
public class IndixServiceImpl implements IndixService {

    /**
     * 用户编号
     */
    private static final Long USER_ID=1L;
    /**
     * 用户所在的城市
     */
    private static final String USER_ADDRESS_CITY="userAddressCity:";
    /**
     * 热搜商品
     */
    private static final String SEARCH_GOODS="searchGoods:";
    /**
     * 用户历史记录
     */
    private static final String USER_HISTORY_GOODS="userHistoryGoods:";
    /**
     * 商品详情
     */
    private static final String GOODS_DETAIL_INFO = "goodsDetailInfo:";
    /**
     * 商品销量
     */
    private static final String SALES_VOLUME="salesVolume:";
    /**
     * 商品评价
     */
    private final static String REMARK_ID = "remark:";
    /**
     * 商品浏览量
     */
    private final static String PRODUCT_VIEW = "productView:";
    IndexMapper indexMapper;
    GoodsVoRepository goodsVoRepository;
    RedisTemplate<String,Object> redisTemplate;
    ValueOperations<String, Object> stringOps;
    ZSetOperations<String, Object> zSetOperations;
    HashOperations<String, Object, Object> hashOperations;
    ListOperations<String,Object> listOperations;
    StoreShellerIndexService storeShellerIndexService;

    @Autowired(required = false)
    public IndixServiceImpl(IndexMapper indexMapper, GoodsVoRepository goodsVoRepository,StoreShellerIndexService storeShellerIndexService ,RedisTemplate<String, Object> redisTemplate) {
        this.indexMapper = indexMapper;
        this.goodsVoRepository = goodsVoRepository;
        this.redisTemplate = redisTemplate;
        this.stringOps = redisTemplate.opsForValue();
        this.zSetOperations = redisTemplate.opsForZSet();
        this.hashOperations = redisTemplate.opsForHash();
        this.listOperations = redisTemplate.opsForList();
        this.storeShellerIndexService = storeShellerIndexService;
    }

    /**
     * 返回首页定位地址
     *
     * @return 返回城市
     */
    @Override
    public String selectAddressCity(String cityName) throws Exception {
        //拿到redis中的地址
        Object o = stringOps.get(USER_ADDRESS_CITY + USER_ID);
        //如果传过来的城市为null
        if (StringUtils.isEmpty(cityName)){
            //如果redis中也没有  抛异常 让前端传定位地址过来
            if (StringUtils.isEmpty(o)){
                throw new FLJBusinessException(FLJExceptionCode.ADDRESS_NULL);
            }
            return o.toString();
        }
        //传过来的城市不为null,但redis中没有，往redis中存，并将改地址返给前端
        if (StringUtils.isEmpty(o)){
            //将地址存入redis  并设置过期时间为7天
            stringOps.set(USER_ADDRESS_CITY + USER_ID,cityName,7, TimeUnit.DAYS);
            return cityName;
        }
        //传过来的城市和redis中的相同直接返回
        if (o.toString().equals(cityName)){
            return cityName;
        }else {
            //传过来的城市和redis中的不同,修改redis  再返回
            stringOps.set(USER_ADDRESS_CITY + USER_ID,cityName,7, TimeUnit.DAYS);
            return cityName;
        }
    }

    /**
     * @return 点开搜索后热点和历史记录
     */
    @Override
    public Map<String, Object> selectHistoryAndHot() {
        Map<String,Object> map = new HashMap<>(16);

        if (!redisTemplate.hasKey(SEARCH_GOODS)){
            //如果没有热搜
            map.put("hotSearch",null);
        }else {
            List<Object> objects = new ArrayList<>();
            //存在取分数前十名
            Set<ZSetOperations.TypedTuple<Object>> typedTuples = zSetOperations.reverseRangeWithScores(SEARCH_GOODS, 0, 9);
            for (ZSetOperations.TypedTuple<Object> typedTuple : typedTuples) {
                //商品名称
               objects.add(typedTuple.getValue());
            }
            map.put("hotSearch",objects);
        }

        //历史记录去前十条
        List<Object> range = listOperations.range(USER_HISTORY_GOODS + USER_ID, 0, 9);
        map.put("historySearch",range);
        return map;
    }

    /**
     * 商品手动预热向redis中添加
     * string  键是商品id  值是一个map
     */
    @Override
    public void addRedisGoods() {
        List<Goods> goods = indexMapper.selectGoodsDatail();
        //根据商品编号存数据
        for (Goods good : goods) {
            stringOps.set(GOODS_DETAIL_INFO+good.getGoodsId(),good);
        }
    }

    @Override
    public List<Map<String, Object>> searchGoods(String name) throws Exception {
        List<Map<String,Object>> list = new ArrayList<>();
        //没有传
        if (StringUtils.isEmpty(name)){
            return null;
        }
        //先删除，再添加（相同的）
        //如果历史搜索中存在相同的，就把前一次的删掉
        listOperations.remove(USER_HISTORY_GOODS+USER_ID,0,name);
        //添加到历史搜索中
        listOperations.leftPush(USER_HISTORY_GOODS+USER_ID,name);
        //添加到热搜中
        zSetOperations.incrementScore(SEARCH_GOODS,name,1);
        //不为null
        List<GoodsVo> goodsVos = goodsVoRepository.findGoodsVoByGoodsNameEqualsOrGoodsTitleEqualsOrGoodsKeyWordEquals(name, name, name);
        //搜索框架上都没有，就直接去数据库上查商品并把商品保存到vo上
        if (goodsVos == null){
            List<GoodsVo> goodsVos1 = indexMapper.selectByName(name);
            if (goodsVos1 == null || goodsVos1.size()<1){
                throw new FLJBusinessException(FLJExceptionCode.GOODS_NOT_EXIST);
            }
            //如果有就把商品返回给前端，并将其存到search中
            for (GoodsVo goodsVo : goodsVos1) {
                //先存到search中
                goodsVoRepository.save(goodsVo);
                //如何返回给前端
                Map<String,Object> map = new HashMap<>(16);
                map.put("goods",goodsVo);
                //取销量
                Double score = zSetOperations.score(SALES_VOLUME, goodsVo.getId());
                if (score == null){
                    score =0d;
                }
                map.put("score",score);
                //获取商品的评价
                Object o = stringOps.get(REMARK_ID + goodsVo.getId());
                if (o==null){
                    map.put("remark",0);
                }else {
                    map.put("remark",o);
                }
                list.add(map);
            }
            return list;
        }
        //如果搜素框架上有，直接返回搜素框架的
        for (GoodsVo goodsVo : goodsVos) {
            //如何返回给前端
            Map<String,Object> map = new HashMap<>(16);
            map.put("goods",goodsVo);
            //取销量
            Double score = zSetOperations.score(SALES_VOLUME, goodsVo.getId());
            if (score == null){
                score =0d;
            }
            map.put("score",score);
            //获取商品的评价
            Object o = stringOps.get(REMARK_ID + goodsVo.getId());
            if (o==null){
                map.put("remark",0);
            }else {
                map.put("remark",o);
            }
            list.add(map);
        }
        return list;
    }

    /**
     * 根据id查商品详情
     * 先查redis  ，查了redis没有，在查数据库 并保存在redis
     * @param goodsId 商品编号
     * @return 返回商品详情
     */
    @Override
    public Map<String,Object> selectByGoodsId(Long goodsId) throws Exception {
        //从redis查数据
        Object o = stringOps.get(GOODS_DETAIL_INFO + goodsId);
        Map<String,Object> map = new HashMap<>(16);
        Goods goods = null;
        //redis中没有  去数据库中查
        if (StringUtils.isEmpty(o)){
            goods = indexMapper.selectByGoodsId(goodsId);
            if (goods==null){
                //数据库中也没有(商品已下架)
                throw new FLJBusinessException(FLJExceptionCode.GOODS_SOLD_OUT);
            }
            //数据库中有，就存到Redis中(普通商品过期时间设置成5到10天的随机数)
            //随机数
            Random random = new Random();
            stringOps.set(GOODS_DETAIL_INFO+goodsId,goods,random.nextInt(6)+5,TimeUnit.DAYS);
        }else {
            //redis中有直接用
            goods = JsonUtil.stringToObject(JsonUtil.objectToString(o),Goods.class);
        }
        map.put("goods",goods);
        //设置浏览量
        zSetOperations.incrementScore(PRODUCT_VIEW+goodsId,goodsId,1);
        //更新其过期时间(7天)
        redisTemplate.expire(PRODUCT_VIEW+goodsId,7,TimeUnit.DAYS);
        //拿到该商品的浏览量
        Double productView = zSetOperations.score(PRODUCT_VIEW + goodsId, goodsId);
        map.put("productView",productView);
        //拿到商品的销量
        Double salesVolume = zSetOperations.score(SALES_VOLUME, goodsId);
        if (salesVolume == null){
            salesVolume = 0d;
        }
        map.put("salesVolume",salesVolume);
        //获得店铺的优惠券
        List<Map<String, Object>> coupon = indexMapper.selectGoodsCoupon(goods.getStoreId());
        map.put("coupon",coupon);
        //查看商家是否开启服务（开启服务为1  没开启为0）
        Map<String, Object> serviceMap = indexMapper.selectStoreService(goods.getStoreId());
        map.put("serviceMap",serviceMap);
        //拿到以下要求的所有key
        Set<String> keys = redisTemplate.keys(REMARK_ID + goodsId + ":*");
        if (keys!=null){
            //评价
            List<Map<String,Object>> remarkList = new ArrayList<>();
            //计数器
            long count = 0L;
            //求和
            long sum = 0L;
            //里面有该该商品的评论时
            for (String key : keys) {
                Object o1 = stringOps.get(key);
                //转换成map
                Map newMap = JsonUtil.stringToObject(String.valueOf(o), Map.class);
                long rank = Long.parseLong(map.get("rank").toString());
                count++;
                sum += rank;
                remarkList.add(map);
            }
            //算评论星级别
            if (count == 0L){
                map.put("pingLunXinXi",0);
                map.put("haoPingLv",0);
            }else {
                map.put("pingLunXinXi",sum/count);
                map.put("haoPingLv",sum/count/5);
            }
            map.put("pingLun",remarkList);

        }
        //拿到某一个商品的掌柜推荐的信息
        List<AssociateGoods> associateGoodsList = goods.getAssociateGoodsList();
        //更具商品编号去查他的简略信息
        if (associateGoodsList!=null){
            List<GoodsVo> goodsVoList = new ArrayList<>();
            for (AssociateGoods associateGoods : associateGoodsList) {
                GoodsVo goodsVo = goodsVoRepository.findGoodsVoById(associateGoods.getAssociatedGoods());
                goodsVoList.add(goodsVo);
            }
            map.put("zhangGuiTuiJian",goodsVoList);
        }else {
            map.put("zhangGuiTuiJian",null);
        }
        //拿到店铺简略信息
        Map<String, Object> storeMap = storeShellerIndexService.selectStoreBasicMessage(goods.getStoreId());
        map.put("storeMap",storeMap);
        return map;
    }


    @Override
    public GoodsVo selectById(Long goodsId) throws Exception{
        GoodsVo goodsVoById = goodsVoRepository.findGoodsVoById(goodsId);
        if (goodsVoById == null){
            //去数据库中查
            goodsVoById = indexMapper.selectByGoodsVoId(goodsId);
            if (goodsVoById == null){
                throw new FLJBusinessException(FLJExceptionCode.GOODS_SOLD_OUT);
            }
        }
        //将这个商品添加到search中并返回给前端
        goodsVoRepository.save(goodsVoById);
        return goodsVoById;
    }


    /**
     * @return 首页热销商品
     */
    @Override
    public List<Map<String, Object>> selectHotSales() throws Exception {
        List<Map<String,Object>> list = new ArrayList<>();
        //先拿到热销排名前十的商品及编号
        Set<ZSetOperations.TypedTuple<Object>> typedTuples = zSetOperations.reverseRangeWithScores(SALES_VOLUME, 0, 9);
        //第一次没有下订单时
        if (typedTuples==null || typedTuples.size()<1){
            //在search中取前10条
            Pageable pageable = PageRequest.of(0,9);
            Page<GoodsVo> goodsVos = goodsVoRepository.findAll(pageable);
            //取到里面集合信息
            List<GoodsVo> goodsVoList = goodsVos.getContent();
            for (GoodsVo goodsVo : goodsVoList) {
                Map<String, Object> map = new HashMap<>(16);
                map.put("xiaoLiang",0);
                map.put("pingJia",0);
                map.put("goods",goodsVo);
                list.add(map);
            }
        }else {
            for (ZSetOperations.TypedTuple<Object> typedTuple : typedTuples) {
                Map<String, Object> map = new HashMap<>(16);
                //商品编号
                Long goodsId = Long.parseLong(typedTuple.getValue().toString());
                //拿到商品的销量
                map.put("xiaoLiang",typedTuple.getScore());
                //拿到评价
                map.put("pingJia",selectGoodsRemark(goodsId));
                map.put("goods",goodsVoRepository.findGoodsVoById(goodsId));
                list.add(map);
            }
        }
        return list;
    }


    /**
     * 拿到商品的评价分数
     * @param goodsId 商品编号
     * @return 返回平均分数
     */
    @Override
    public Long selectGoodsRemark(Long goodsId) {
        //去查询所有评价
        Set<String> keys = redisTemplate.keys(REMARK_ID + goodsId + ":*");
        if (keys == null){
            return 0L;
        }
        //计数器
        long count = 0L;
        //求和
        long sum = 0L;
        for (String key : keys) {
            String s = String.valueOf(stringOps.get(key));
            Map map = JsonUtil.stringToObject(s, Map.class);
            count++;
            Long rank = Long.parseLong(map.get("rank").toString());
            sum+=rank;
        }
        if (count==0){
            return 0L;
        }
        return sum/count;
    }
    /**
     * 掌柜推荐
     * @param typeName 类型
     * @return 返回具体商品
     */
    @Override
    public List<Map<String, Object>> selectGoodsIsRecommend(String typeName) {
        if (StringUtils.isEmpty(typeName)){
            typeName="夏季水果";
        }
        List<Map<String,Object>> list = new ArrayList<>();
        //返回第一页10条
        Page<GoodsVo> goodsVos = goodsVoRepository.findGoodsVoByGoodsFirstItemNameAndGoodsIsRecommend(typeName, true, PageRequest.of(0, 9));
        List<GoodsVo> content = goodsVos.getContent();
        for (GoodsVo goodsVo : content) {
            Map<String,Object> map = new HashMap<>(16);
            Double score = zSetOperations.score(SALES_VOLUME, goodsVo.getId());
            if (score == null){
                score = 0d;
            }
            map.put("xiaoLiang",score);
            Long rank = selectGoodsRemark(goodsVo.getId());
            if (rank==null){
                rank = 0L;
            }
            map.put("pingJia",rank);
            map.put("goods",goodsVo);
            list.add(map);
        }
        return list;
    }
}
